package org.zoomdev.zoom.web.action;

import org.zoomdev.zoom.common.Destroyable;
import org.zoomdev.zoom.common.caster.Caster;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.web.parameter.PreParameter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Action上下文，保存一次http请求的上下文
 * 其中比较重要的属性是state,表示对action的处理进度
 * 本对象的生命周期为一次请求(Request)
 *
 * @author jzoom
 */
public final class ActionContext implements Destroyable {
    /**
     * 初始化阶段，对request的解析还未开始
     */
    public static final int STATE_INIT = 0;

    /**
     * 预处理阶段,可以判断登录信息等，这个阶段将解析request中的数据，如将json字符串转为Map输入对象
     */
    public static final int STATE_PRE_PARSE = 1;

    /**
     * request的输入流已经解析了，这个阶段会将输入对象转成调用方法的参数数组
     */
    public static final int STATE_PARSE = 2;

    /**
     * 在调用方法之前
     */
    public static final int STATE_BEFORE_INVOKE = 3;

    /**
     * 在渲染之前
     */
    public static final int STATE_BEFORE_RENDER = 4;

    /**
     * 在渲染之后
     */
    public static final int STATE_AFTER_RENDER = 5;


    private final HttpServletRequest request;
    private final HttpServletResponse response;
    private final MethodActionHandler action;
    private int state;
    /**
     * 参数预处理结果，这里保存的是STATE_PRE_PARSE阶段对参数做转换的结果，框架目前支持三种转换
     * 1、对于json，转换成Map
     * 2、对于form-data，转换成Map
     * 3、对于其他请求，转换成HttpServletRequest
     */
    private PreParameter preParameter;
    /**
     * 用来调用Controller定义的方法，与方法参数一致
     */
    private Object[] args;
    /**
     * Controller方法返回值
     */
    private Object result;
    /**
     * Controller的实例
     */
    private Object target;
    /**
     * 整个{@line ActionContext}执行期间的异常
     */
    private Throwable exception;
    /**
     * 这里可以在Request期间存放一些数据
     */
    private HashMap<String, Object> data;
    /**
     * 需要渲染的对象
     */
    private Object renderObject;
    private String contentType;

    public ActionContext(HttpServletRequest request,
                         HttpServletResponse response,
                         MethodActionHandler action) {
        if (request == null || response == null || action == null) {
            throw new IllegalArgumentException("有参数为null");
        }
        this.request = request;
        this.response = response;
        this.action = action;
    }


    public String toString() {
        return request.getServletPath();
    }

    /**
     * 与put是同义词
     *
     * @param key
     * @param value
     * @return
     */
    public ActionContext set(String key, Object value) {
        checkData().put(key, value);
        return this;
    }

    /**
     * set的同义词
     *
     * @param key
     * @param value
     * @return
     */
    public ActionContext put(String key, Object value) {
        return set(key, value);
    }

    public <T> T get(String key, Class<T> classOfT) {
        return Caster.to(checkData().get(key), classOfT);
    }

    /**
     * 确定类型可以强制转化
     *
     * @param key
     * @param <T>
     * @return
     */
    public <T> T get(String key) {
        return (T) checkData().get(key);
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
        this.exception = null;
        setState(STATE_BEFORE_RENDER);
    }

    public PreParameter getPreParameter() {
        return preParameter;
    }

    public void setPreParameter(PreParameter preParameter) {
        this.preParameter = preParameter;
        setState(STATE_PARSE);
    }

    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
        setState(STATE_BEFORE_INVOKE);
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public Throwable getException() {
        return this.exception;
    }

    public void setException(Throwable e) {
        this.exception = e;
    }

    public Object getRenderObject() {
        if (renderObject != null) {
            return renderObject;
        }
        if (exception != null) {
            return exception;
        }
        return result;
    }

    /**
     * 这个方法将直接设置要渲染的对象，并且将状态改成 {@link ActionContext#STATE_BEFORE_RENDER}
     *
     * @param renderObject
     */
    public void setRenderObject(Object renderObject) {
        this.renderObject = renderObject;
        this.result = renderObject;
        this.exception = null;
        setState(STATE_BEFORE_RENDER);
    }

    public ActionContext setSession(String key, Object value) {
        HttpSession session = null;
        if (request != null && ((session = request.getSession()) != null)) {
            try {
                session.setAttribute(key, value);
            } catch (Throwable t) {

            }
        }
        return this;
    }

    public ActionContext removeSession(String key) {
        HttpSession session;
        if (((session = request.getSession()) != null)) {
            try {
                session.removeAttribute(key);
            } catch (Throwable t) {

            }
        }
        return this;
    }


    public String getContentType() {
        if (contentType == null) {
            contentType = getRequest().getContentType();
        }
        return contentType;
    }

    /**
     * 有可能获取到null
     *
     * @return
     */
    public Cookie[] getCookies() {
        return request.getCookies();
    }

    /**
     * 有可能获取到null
     *
     * @return
     */
    public String getSessionId() {
        HttpSession session;
        if (((session = request.getSession()) != null)) {
            try {
                return session.getId();
            } catch (Throwable t) {

            }
        }
        return null;
    }

    public Object getSession(String key) {
        HttpSession session;
        if (((session = request.getSession()) != null)) {
            try {
                return session.getAttribute(key);
            } catch (Throwable t) {

            }
        }
        return null;
    }

    public HttpServletResponse getResponse() {
        return response;
    }


    public HttpServletRequest getRequest() {
        return request;
    }


    public MethodActionHandler getAction() {
        return action;
    }


    public Map<String, Object> getData() {
        return data;
    }

    @SuppressWarnings("unchecked")
    public <T> T getTarget() {
        return (T) target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }


    public void destroy() {
        if (this.data != null) {
            Classes.destroy(this.data);
            this.data = null;
        }
        exception = null;
        renderObject = null;
        result = null;
        preParameter = null;
        target = null;
        if (args != null) {
            Arrays.fill(args, null);
            args = null;
        }
    }

    private Map<String, Object> checkData() {
        if (data == null) {
            data = new HashMap<>();
        }
        return data;
    }
}
